package com.yc.thread.fairNofairLock;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 1. Create an object of ReentrantLock
 * 2. Create a worker(Runnable Object) to execute and pass the lock to the object
 * 3. Use the lock() method to acquire the lock on shared resource
 * 4. After completing work, call unlock() method to release the lock
 * @author samuelchen
 * @version 1.0
 * @date 2022-02-15
 */
public class Worker implements Runnable{

    String name;
    ReentrantLock lock;

    public Worker(String name, ReentrantLock lock) {
        this.name = name;
        this.lock = lock;
    }

    @Override
    public void run() {
        boolean done = false;
        while (!done) {
            boolean ans = lock.tryLock();
            if (ans) {
                try {
                    Date d = new Date();
                    SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
                    System.out.println("task name - " + name
                            + " outer lock acquired at "
                            + sdf.format(d)
                            + " Doing outer work");
                    Thread.sleep(1500L);
                    // Getting Inner Lock
                    lock.lock();
                    try {
                        d = new Date();
                        sdf = new SimpleDateFormat("hh:mm:ss");
                        System.out.println("task name - "+ name
                                + " inner lock acquired at "
                                + sdf.format(d)
                                + " Doing inner work");
                        System.out.println("Lock Hold Count - "+ lock.getHoldCount());
                        Thread.sleep(1500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        System.out.println("task name -" + name + "releasing inner lock");
                        lock.unlock();
                    }
                    System.out.println("Lock Hold Count - " + lock.getHoldCount());
                    System.out.println("task name - " + name + " work done");

                    done = true;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //Outer lock release
                    System.out.println("task name - " + name +
                            " releasing outer lock");

                    lock.unlock();
                    System.out.println("Lock Hold Count - " +
                            lock.getHoldCount());
                }
            } else {
                System.out.println("task name - " + name +
                        " waiting for lock");
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static final int MAX_T = 2;

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        ExecutorService executorService = new ThreadPoolExecutor(MAX_T, MAX_T,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
        Runnable r1 = new Worker("job1", lock);
        Runnable r2 = new Worker("job2", lock);
        Runnable r3 = new Worker("job3", lock);
        Runnable r4 = new Worker("job4", lock);
        executorService.execute(r1);
        executorService.execute(r2);
        executorService.execute(r3);
        executorService.execute(r4);
        executorService.shutdown();

    }
}
